1 Transcriptional profile PCA and differential gene expression (DGA)

1.1 RNA-seq

RNA was isolated and sequenced from the various cell types.

1.2 Sample summary

sample_table <- readxl::read_xlsx('Key.xlsx')
sample_table <- data.frame(sample_table)
rownames(sample_table) <- sample_table$INDEXED.SAMPLE.NAME
sample_table$INDEXED.SAMPLE.NAME <-NULL
sample_table$GROUP.NAME <- c("D_StemB","D_StemB","D_StemB","D_ProB","D_ProB","D_ProB","D_ProB","R_StemB_StemB","R_StemB_StemB","R_StemB_StemB","R_StemB_StemB","R_StemB_StemB","R_StemB_ProB","R_StemB_ProB","R_StemB_ProB","R_StemB_ProB","R_ProB_ProB","R_ProB_ProB","R_ProB_ProB","R_ProB_ProB","R_ProB_ProB","R_ProB_StemB","R_ProB_StemB","R_ProB_StemB","R_ProB_StemB","R_ProB_StemB")
sample_table<-rownames_to_column(sample_table,"File.ID")
datatable(sample_table) %>% 
  formatStyle("GROUP.NAME", backgroundColor = styleEqual(c("D_StemB","D_ProB","R_StemB_StemB","R_StemB_ProB","R_ProB_ProB","R_ProB_StemB"), c("#EAD3BF","#AA9486", "#B6854D","#9986A5","#79402E","#CC79A7")))

1.2.1 Using scater normalization

# big_counts <- paste0("myriad/",rownames(sample_table),"/replicate_1/qc/",rownames(sample_table), "_QC.summary.txt/QC.geneCounts.formatted.for.DESeq.txt.gz") %>% map(gzfile) %>% map(read.table, stringsAsFactors = FALSE, sep = "\t")  %>% reduce(left_join, by = "V1") 
# 
# colnames(big_counts) <- c("ensgene", rownames(sample_table))
# rownames(big_counts) <- big_counts$ensgene
# big_counts$ensgene <- NULL
# big_counts <- big_counts[!is.na(rowSums(big_counts)),]
# saveRDS(big_counts,"big_counts.rds")
big_counts <- read.table('myriad/temp2',stringsAsFactors = FALSE, header = TRUE)
count_matrix <- as.matrix(big_counts)
colnames(count_matrix) <- rownames(sample_table)
rownames(count_matrix) <- rownames(big_counts)

sce <- SingleCellExperiment(list(counts = count_matrix),
                            colData = sample_table)
sce <- sce[rowSums(counts(sce)) >  0,]
sce <- calculateQCMetrics(sce)
## prepare total count and total features data

my_df <- data.frame("sample_id" = colnames(sce), "total_counts" = sce$total_counts,
                    "total_features" = sce$total_features_by_counts )
ggplot(my_df,aes(total_counts)) + geom_histogram(bins = 10) + 
  theme_minimal() + ggtitle("Histogram of total counts") + ylab("number of samples") + xlab("total counts")

ggplot(my_df,aes(total_features)) + geom_histogram(bins = 10) + 
  theme_minimal() + ggtitle("Histogram of total features") + ylab("number of samples") + xlab("total features")

1.3 DEseq2

dds <- DESeqDataSetFromMatrix(big_counts, colData = sample_table, design = ~GROUP.NAME)
my_vst <- vst(dds)

pcaData <- DESeq2::plotPCA(my_vst, intgroup =c("DISEASE.STAGE","INJECTED.POPULATION"),returnData = TRUE)
percentVar <- round(100 * attr(pcaData, "percentVar"))

ggplot(pcaData,aes(PC1,PC2,colour = DISEASE.STAGE, shape = INJECTED.POPULATION)) + geom_point(size = 3 ) +
        xlab(paste0("PC1: ",percentVar[1],"% variance")) +
        ylab(paste0("PC2: ",percentVar[2],"% variance")) + geom_text(data=subset(pcaData,PC1 > 50), aes(PC1,PC2,label = name),nudge_x = -16) +
        theme_minimal()

1.4 DEseq2 outlier removed

big_counts <- big_counts[,grep("VT40_N708_S502",colnames(big_counts),invert = T)]
sample_table <- sample_table[grep("VT40_N708_S502", sample_table$File.ID,invert = T),]

dds <- DESeqDataSetFromMatrix(big_counts, colData = sample_table, design = ~GROUP.NAME)
my_vst <- vst(dds)

pcaData <- DESeq2::plotPCA(my_vst, intgroup =c("DISEASE.STAGE","INJECTED.POPULATION"),returnData = TRUE)
percentVar <- round(100 * attr(pcaData, "percentVar"))

ggplot(pcaData,aes(PC1,PC2,colour = DISEASE.STAGE, shape = INJECTED.POPULATION)) + geom_point(size = 3 ) +
        xlab(paste0("PC1: ",percentVar[1],"% variance")) +
        ylab(paste0("PC2: ",percentVar[2],"% variance")) + geom_text(data=subset(pcaData,PC1 > 50), aes(PC1,PC2,label = name),nudge_x = -16) +
        theme_minimal()

1.5 PCA GSEA

1.5.1 PC1 GSEA

hallmark <- gmtPathways("~/genome_apps/GSEA/h.all.v6.2.symbols.gmt")
pca1_gsea = function(object) {
  # calculate the variance for each gene
  rv <- rowVars(assay(object))

  # select the 1000 top genes by variance
  select <- order(rv, decreasing=TRUE)[seq_len(min(2000, length(rv)))]

  # perform a PCA on the data in assay(x) for the selected genes
  pca <- prcomp(t(assay(object)[select,]))

  PCA1_contrib <- sort(pca$rotation[,1], decreasing = TRUE )
  PCA1_contrib <- data.frame("ensgene" = names(PCA1_contrib), PCA1_contrib)
  PCA1_contrib <- left_join(PCA1_contrib,grch38,by="ensgene")
  PCA1_contrib <- PCA1_contrib[complete.cases(PCA1_contrib$symbol),]
  for_gsea <- PCA1_contrib$PCA1_contrib  
  names(for_gsea) <- PCA1_contrib$symbol
  fgseaRes <- fgsea(hallmark, for_gsea, nperm=10000, maxSize = 500)
  fgseaRes <- fgseaRes[fgseaRes$padj < 0.05,]
  plotGseaTable(hallmark[fgseaRes$pathway], for_gsea, fgseaRes, gseaParam = 0.4)
  return(PCA1_contrib)
}
pc1_res<-pca1_gsea(my_vst)

1.5.2 Genes Driving PC1

datatable(pc1_res,options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))

1.5.3 PC2 GSEA

pca2_gsea = function(object) {
  # calculate the variance for each gene
  rv <- rowVars(assay(object))

  # select the 1000 top genes by variance
  select <- order(rv, decreasing=TRUE)[seq_len(min(2000, length(rv)))]

  # perform a PCA on the data in assay(x) for the selected genes
  pca <- prcomp(t(assay(object)[select,]))

  PCA1_contrib <- sort(pca$rotation[,2], decreasing = TRUE )
  PCA1_contrib <- data.frame("ensgene" = names(PCA1_contrib), PCA1_contrib)
  PCA1_contrib <- left_join(PCA1_contrib,grch38,by="ensgene")
  PCA1_contrib <- PCA1_contrib[complete.cases(PCA1_contrib$symbol),]
  for_gsea <- PCA1_contrib$PCA1_contrib  
  names(for_gsea) <- PCA1_contrib$symbol
  fgseaRes <- fgsea(hallmark, for_gsea, nperm=10000, maxSize = 500)
  fgseaRes <- fgseaRes[fgseaRes$padj < 0.05,]
  plotGseaTable(hallmark[fgseaRes$pathway], for_gsea, fgseaRes, gseaParam = 0.4)
  return(PCA1_contrib)
}
pca2_res <- pca2_gsea(my_vst)

1.5.4 Genes Driving PC2

datatable(pca2_res,options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))

1.6 combinations

library(gtools)
my_combinations <- data.frame(combinations(n=6,r=2,v=unique(sample_table$GROUP.NAME)),stringsAsFactors = F)

colnames(my_combinations) <- c("first","second")

datatable(my_combinations)

1.7 Differential Gene Expression

1.7.1 1.

# deseq <- DESeq(dds)
# saveRDS(deseq,"vt40_deseq.rds")
deseq <- readRDS("vt40_deseq.rds")

DGE <- function(c1,c2){ 
res <- results(deseq,contrast = c("GROUP.NAME", c1, c2))
resOrdered <- res[order(res$padj),]
resOrdered <- resOrdered[complete.cases(resOrdered),]
resOrdered <- data.frame(resOrdered)
resOrdered <- left_join(rownames_to_column(resOrdered,var = "ensgene"), grch38, by = "ensgene") %>% select(symbol,log2FoldChange,padj)

res_for_table <- resOrdered  %>% dplyr::filter(padj < 0.1)

return(list(resOrdered=resOrdered,res_for_table=res_for_table))
}

my_res<-DGE(my_combinations$first[1],my_combinations$second[1])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[1]," vs ",my_combinations$second[1]),
          options = list(dom = 'Bfrtip',
                         buttons = c('copy', 'csv', 'excel')))
plot_gsea <- function(res){
res <- res[complete.cases(res),]
res$fcSign <- sign(res$log2FoldChange)
res$logP <- -log10(res$padj)
res$metric <- res$logP/res$fcSign
y<-res[,c("symbol", "metric")]
geneList <- y$metric
names(geneList) <- y$symbol
geneList <- geneList[order(geneList, decreasing = T)]
fgseaRes <- fgsea(hallmark, geneList, nperm=10000, maxSize = 500)
fgseaRes <- fgseaRes[fgseaRes$padj < 0.05,]
plotGseaTable(hallmark[fgseaRes$pathway], geneList, fgseaRes, gseaParam = 0.4)
}

plot_gsea(my_res$resOrdered)

1.7.2 2.

my_res<-DGE(my_combinations$first[2],my_combinations$second[2])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[2]," vs ",my_combinations$second[2]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.3 3.

my_res<-DGE(my_combinations$first[3],my_combinations$second[3])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[3]," vs ",my_combinations$second[3]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.4 4.

my_res<-DGE(my_combinations$first[4],my_combinations$second[4])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[4]," vs ",my_combinations$second[4]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.5 5.

my_res<-DGE(my_combinations$first[5],my_combinations$second[5])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[5]," vs ",my_combinations$second[5]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.6 6.

my_res<-DGE(my_combinations$first[6],my_combinations$second[6])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[6]," vs ",my_combinations$second[6]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.7 7.

my_res<-DGE(my_combinations$first[7],my_combinations$second[7])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[7]," vs ",my_combinations$second[7]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.8 8.

my_res<-DGE(my_combinations$first[8],my_combinations$second[8])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[8]," vs ",my_combinations$second[8]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.9 9.

my_res<-DGE(my_combinations$first[9],my_combinations$second[9])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[9]," vs ",my_combinations$second[9]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.10 10.

my_res<-DGE(my_combinations$first[10],my_combinations$second[10])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[10]," vs ",my_combinations$second[10]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.11 11.

my_res<-DGE(my_combinations$first[11],my_combinations$second[11])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[11]," vs ",my_combinations$second[11]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.12 12.

my_res<-DGE(my_combinations$first[12],my_combinations$second[12])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[12]," vs ",my_combinations$second[12]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.13 13.

my_res<-DGE(my_combinations$first[13],my_combinations$second[13])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[13]," vs ",my_combinations$second[13]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.14 14.

my_res<-DGE(my_combinations$first[14],my_combinations$second[14])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[14]," vs ",my_combinations$second[14]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

1.7.15 15.

my_res<-DGE(my_combinations$first[15],my_combinations$second[15])
datatable(my_res$res_for_table, extensions = 'Buttons',
          caption = paste(my_combinations$first[15]," vs ",my_combinations$second[15]),
          options = list(dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
plot_gsea(my_res$resOrdered)

LS0tCnRpdGxlOiAiRXhwbG9yaW5nIFJhaidzIFNhbXBsZXMiCmF1dGhvcjogPGEgaHJlZj0iaHR0cHM6Ly9jaGVsYS1qYW1lcy5naXRodWIuaW8vIj4gPGgzPiBDaGVsYSBKYW1lcyA8L2gzPiA8L2E+IFxuZXdsaW5lIENhbmNlciBJbnN0aXR1dGUsIFVDTCwgVUsKZGF0ZTogMTUgQXBybCAyMDE5Cm91dHB1dDoKICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGRmX3ByaW50OiBwYWdlZAogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogICAga2VlcF9tZDogZmFsc2UKICAgIGVuY29kaW5nOiAiVVRGLTgiCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpodG1sdG9vbHM6OnRhZ0xpc3Qocm1hcmtkb3duOjpodG1sX2RlcGVuZGVuY3lfZm9udF9hd2Vzb21lKCkpCmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gVFJVRSwgY2FjaGUgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCmBgYAoKCmBgYHtyLCAgb3V0LndpZHRoID0gIjEwMCUiLCBlY2hvID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeShrbml0cikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHN2YSkKbGlicmFyeShkcGx5cikKbGlicmFyeShwcmVwcm9jZXNzQ29yZSkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KEJpb2Jhc2UpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KHNjYXRlcikKbGlicmFyeShnZ2hpZ2hsaWdodCkKbGlicmFyeShhbm5vdGFibGVzKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShmZ3NlYSkKbGlicmFyeShEVCkKbGlicmFyeShwdXJycikKYGBgCgoKIyBUcmFuc2NyaXB0aW9uYWwgcHJvZmlsZSBQQ0EgYW5kIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gKERHQSkKCiMjIFJOQS1zZXEKClJOQSB3YXMgaXNvbGF0ZWQgYW5kIHNlcXVlbmNlZCBmcm9tIHRoZSB2YXJpb3VzIGNlbGwgdHlwZXMuCgojIyBTYW1wbGUgc3VtbWFyeQoKYGBge3J9CnNhbXBsZV90YWJsZSA8LSByZWFkeGw6OnJlYWRfeGxzeCgnS2V5Lnhsc3gnKQpzYW1wbGVfdGFibGUgPC0gZGF0YS5mcmFtZShzYW1wbGVfdGFibGUpCnJvd25hbWVzKHNhbXBsZV90YWJsZSkgPC0gc2FtcGxlX3RhYmxlJElOREVYRUQuU0FNUExFLk5BTUUKc2FtcGxlX3RhYmxlJElOREVYRUQuU0FNUExFLk5BTUUgPC1OVUxMCnNhbXBsZV90YWJsZSRHUk9VUC5OQU1FIDwtIGMoIkRfU3RlbUIiLCJEX1N0ZW1CIiwiRF9TdGVtQiIsIkRfUHJvQiIsIkRfUHJvQiIsIkRfUHJvQiIsIkRfUHJvQiIsIlJfU3RlbUJfU3RlbUIiLCJSX1N0ZW1CX1N0ZW1CIiwiUl9TdGVtQl9TdGVtQiIsIlJfU3RlbUJfU3RlbUIiLCJSX1N0ZW1CX1N0ZW1CIiwiUl9TdGVtQl9Qcm9CIiwiUl9TdGVtQl9Qcm9CIiwiUl9TdGVtQl9Qcm9CIiwiUl9TdGVtQl9Qcm9CIiwiUl9Qcm9CX1Byb0IiLCJSX1Byb0JfUHJvQiIsIlJfUHJvQl9Qcm9CIiwiUl9Qcm9CX1Byb0IiLCJSX1Byb0JfUHJvQiIsIlJfUHJvQl9TdGVtQiIsIlJfUHJvQl9TdGVtQiIsIlJfUHJvQl9TdGVtQiIsIlJfUHJvQl9TdGVtQiIsIlJfUHJvQl9TdGVtQiIpCnNhbXBsZV90YWJsZTwtcm93bmFtZXNfdG9fY29sdW1uKHNhbXBsZV90YWJsZSwiRmlsZS5JRCIpCmRhdGF0YWJsZShzYW1wbGVfdGFibGUpICU+JSAKICBmb3JtYXRTdHlsZSgiR1JPVVAuTkFNRSIsIGJhY2tncm91bmRDb2xvciA9IHN0eWxlRXF1YWwoYygiRF9TdGVtQiIsIkRfUHJvQiIsIlJfU3RlbUJfU3RlbUIiLCJSX1N0ZW1CX1Byb0IiLCJSX1Byb0JfUHJvQiIsIlJfUHJvQl9TdGVtQiIpLCBjKCIjRUFEM0JGIiwiI0FBOTQ4NiIsICIjQjY4NTREIiwiIzk5ODZBNSIsIiM3OTQwMkUiLCIjQ0M3OUE3IikpKQpgYGAKCiMjIyBVc2luZyBbc2NhdGVyXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvc2NhdGVyLmh0bWwpIG5vcm1hbGl6YXRpb24gCgpgYGB7cn0KIyBiaWdfY291bnRzIDwtIHBhc3RlMCgibXlyaWFkLyIscm93bmFtZXMoc2FtcGxlX3RhYmxlKSwiL3JlcGxpY2F0ZV8xL3FjLyIscm93bmFtZXMoc2FtcGxlX3RhYmxlKSwgIl9RQy5zdW1tYXJ5LnR4dC9RQy5nZW5lQ291bnRzLmZvcm1hdHRlZC5mb3IuREVTZXEudHh0Lmd6IikgJT4lIG1hcChnemZpbGUpICU+JSBtYXAocmVhZC50YWJsZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFLCBzZXAgPSAiXHQiKSAgJT4lIHJlZHVjZShsZWZ0X2pvaW4sIGJ5ID0gIlYxIikgCiMgCiMgY29sbmFtZXMoYmlnX2NvdW50cykgPC0gYygiZW5zZ2VuZSIsIHJvd25hbWVzKHNhbXBsZV90YWJsZSkpCiMgcm93bmFtZXMoYmlnX2NvdW50cykgPC0gYmlnX2NvdW50cyRlbnNnZW5lCiMgYmlnX2NvdW50cyRlbnNnZW5lIDwtIE5VTEwKIyBiaWdfY291bnRzIDwtIGJpZ19jb3VudHNbIWlzLm5hKHJvd1N1bXMoYmlnX2NvdW50cykpLF0KIyBzYXZlUkRTKGJpZ19jb3VudHMsImJpZ19jb3VudHMucmRzIikKYmlnX2NvdW50cyA8LSByZWFkLnRhYmxlKCdteXJpYWQvdGVtcDInLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgaGVhZGVyID0gVFJVRSkKY291bnRfbWF0cml4IDwtIGFzLm1hdHJpeChiaWdfY291bnRzKQpjb2xuYW1lcyhjb3VudF9tYXRyaXgpIDwtIHJvd25hbWVzKHNhbXBsZV90YWJsZSkKcm93bmFtZXMoY291bnRfbWF0cml4KSA8LSByb3duYW1lcyhiaWdfY291bnRzKQoKc2NlIDwtIFNpbmdsZUNlbGxFeHBlcmltZW50KGxpc3QoY291bnRzID0gY291bnRfbWF0cml4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBzYW1wbGVfdGFibGUpCnNjZSA8LSBzY2Vbcm93U3Vtcyhjb3VudHMoc2NlKSkgPiAgMCxdCnNjZSA8LSBjYWxjdWxhdGVRQ01ldHJpY3Moc2NlKQoKYGBgCgpgYGB7cn0KIyMgcHJlcGFyZSB0b3RhbCBjb3VudCBhbmQgdG90YWwgZmVhdHVyZXMgZGF0YQoKbXlfZGYgPC0gZGF0YS5mcmFtZSgic2FtcGxlX2lkIiA9IGNvbG5hbWVzKHNjZSksICJ0b3RhbF9jb3VudHMiID0gc2NlJHRvdGFsX2NvdW50cywKICAgICAgICAgICAgICAgICAgICAidG90YWxfZmVhdHVyZXMiID0gc2NlJHRvdGFsX2ZlYXR1cmVzX2J5X2NvdW50cyApCmdncGxvdChteV9kZixhZXModG90YWxfY291bnRzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTApICsgCiAgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiSGlzdG9ncmFtIG9mIHRvdGFsIGNvdW50cyIpICsgeWxhYigibnVtYmVyIG9mIHNhbXBsZXMiKSArIHhsYWIoInRvdGFsIGNvdW50cyIpCmBgYAoKYGBge3J9CmdncGxvdChteV9kZixhZXModG90YWxfZmVhdHVyZXMpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMCkgKyAKICB0aGVtZV9taW5pbWFsKCkgKyBnZ3RpdGxlKCJIaXN0b2dyYW0gb2YgdG90YWwgZmVhdHVyZXMiKSArIHlsYWIoIm51bWJlciBvZiBzYW1wbGVzIikgKyB4bGFiKCJ0b3RhbCBmZWF0dXJlcyIpCgpgYGAKCiMjIERFc2VxMgoKYGBge3J9CmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGJpZ19jb3VudHMsIGNvbERhdGEgPSBzYW1wbGVfdGFibGUsIGRlc2lnbiA9IH5HUk9VUC5OQU1FKQpteV92c3QgPC0gdnN0KGRkcykKCnBjYURhdGEgPC0gREVTZXEyOjpwbG90UENBKG15X3ZzdCwgaW50Z3JvdXAgPWMoIkRJU0VBU0UuU1RBR0UiLCJJTkpFQ1RFRC5QT1BVTEFUSU9OIikscmV0dXJuRGF0YSA9IFRSVUUpCnBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKQoKZ2dwbG90KHBjYURhdGEsYWVzKFBDMSxQQzIsY29sb3VyID0gRElTRUFTRS5TVEFHRSwgc2hhcGUgPSBJTkpFQ1RFRC5QT1BVTEFUSU9OKSkgKyBnZW9tX3BvaW50KHNpemUgPSAzICkgKwogICAgICAgIHhsYWIocGFzdGUwKCJQQzE6ICIscGVyY2VudFZhclsxXSwiJSB2YXJpYW5jZSIpKSArCiAgICAgICAgeWxhYihwYXN0ZTAoIlBDMjogIixwZXJjZW50VmFyWzJdLCIlIHZhcmlhbmNlIikpICsgZ2VvbV90ZXh0KGRhdGE9c3Vic2V0KHBjYURhdGEsUEMxID4gNTApLCBhZXMoUEMxLFBDMixsYWJlbCA9IG5hbWUpLG51ZGdlX3ggPSAtMTYpICsKICAgICAgICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKIyMgREVzZXEyIG91dGxpZXIgcmVtb3ZlZAoKYGBge3J9CgpiaWdfY291bnRzIDwtIGJpZ19jb3VudHNbLGdyZXAoIlZUNDBfTjcwOF9TNTAyIixjb2xuYW1lcyhiaWdfY291bnRzKSxpbnZlcnQgPSBUKV0Kc2FtcGxlX3RhYmxlIDwtIHNhbXBsZV90YWJsZVtncmVwKCJWVDQwX043MDhfUzUwMiIsIHNhbXBsZV90YWJsZSRGaWxlLklELGludmVydCA9IFQpLF0KCmRkcyA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGJpZ19jb3VudHMsIGNvbERhdGEgPSBzYW1wbGVfdGFibGUsIGRlc2lnbiA9IH5HUk9VUC5OQU1FKQpteV92c3QgPC0gdnN0KGRkcykKCnBjYURhdGEgPC0gREVTZXEyOjpwbG90UENBKG15X3ZzdCwgaW50Z3JvdXAgPWMoIkRJU0VBU0UuU1RBR0UiLCJJTkpFQ1RFRC5QT1BVTEFUSU9OIikscmV0dXJuRGF0YSA9IFRSVUUpCnBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKQoKZ2dwbG90KHBjYURhdGEsYWVzKFBDMSxQQzIsY29sb3VyID0gRElTRUFTRS5TVEFHRSwgc2hhcGUgPSBJTkpFQ1RFRC5QT1BVTEFUSU9OKSkgKyBnZW9tX3BvaW50KHNpemUgPSAzICkgKwogICAgICAgIHhsYWIocGFzdGUwKCJQQzE6ICIscGVyY2VudFZhclsxXSwiJSB2YXJpYW5jZSIpKSArCiAgICAgICAgeWxhYihwYXN0ZTAoIlBDMjogIixwZXJjZW50VmFyWzJdLCIlIHZhcmlhbmNlIikpICsgZ2VvbV90ZXh0KGRhdGE9c3Vic2V0KHBjYURhdGEsUEMxID4gNTApLCBhZXMoUEMxLFBDMixsYWJlbCA9IG5hbWUpLG51ZGdlX3ggPSAtMTYpICsKICAgICAgICB0aGVtZV9taW5pbWFsKCkKCmBgYAoKIyMgUENBIEdTRUEKCiMjIyBQQzEgR1NFQQoKYGBge3J9CmhhbGxtYXJrIDwtIGdtdFBhdGh3YXlzKCJ+L2dlbm9tZV9hcHBzL0dTRUEvaC5hbGwudjYuMi5zeW1ib2xzLmdtdCIpCnBjYTFfZ3NlYSA9IGZ1bmN0aW9uKG9iamVjdCkgewogICMgY2FsY3VsYXRlIHRoZSB2YXJpYW5jZSBmb3IgZWFjaCBnZW5lCiAgcnYgPC0gcm93VmFycyhhc3NheShvYmplY3QpKQoKICAjIHNlbGVjdCB0aGUgMTAwMCB0b3AgZ2VuZXMgYnkgdmFyaWFuY2UKICBzZWxlY3QgPC0gb3JkZXIocnYsIGRlY3JlYXNpbmc9VFJVRSlbc2VxX2xlbihtaW4oMjAwMCwgbGVuZ3RoKHJ2KSkpXQoKICAjIHBlcmZvcm0gYSBQQ0Egb24gdGhlIGRhdGEgaW4gYXNzYXkoeCkgZm9yIHRoZSBzZWxlY3RlZCBnZW5lcwogIHBjYSA8LSBwcmNvbXAodChhc3NheShvYmplY3QpW3NlbGVjdCxdKSkKCiAgUENBMV9jb250cmliIDwtIHNvcnQocGNhJHJvdGF0aW9uWywxXSwgZGVjcmVhc2luZyA9IFRSVUUgKQogIFBDQTFfY29udHJpYiA8LSBkYXRhLmZyYW1lKCJlbnNnZW5lIiA9IG5hbWVzKFBDQTFfY29udHJpYiksIFBDQTFfY29udHJpYikKICBQQ0ExX2NvbnRyaWIgPC0gbGVmdF9qb2luKFBDQTFfY29udHJpYixncmNoMzgsYnk9ImVuc2dlbmUiKQogIFBDQTFfY29udHJpYiA8LSBQQ0ExX2NvbnRyaWJbY29tcGxldGUuY2FzZXMoUENBMV9jb250cmliJHN5bWJvbCksXQogIGZvcl9nc2VhIDwtIFBDQTFfY29udHJpYiRQQ0ExX2NvbnRyaWIgIAogIG5hbWVzKGZvcl9nc2VhKSA8LSBQQ0ExX2NvbnRyaWIkc3ltYm9sCiAgZmdzZWFSZXMgPC0gZmdzZWEoaGFsbG1hcmssIGZvcl9nc2VhLCBucGVybT0xMDAwMCwgbWF4U2l6ZSA9IDUwMCkKICBmZ3NlYVJlcyA8LSBmZ3NlYVJlc1tmZ3NlYVJlcyRwYWRqIDwgMC4wNSxdCiAgcGxvdEdzZWFUYWJsZShoYWxsbWFya1tmZ3NlYVJlcyRwYXRod2F5XSwgZm9yX2dzZWEsIGZnc2VhUmVzLCBnc2VhUGFyYW0gPSAwLjQpCiAgcmV0dXJuKFBDQTFfY29udHJpYikKfQpwYzFfcmVzPC1wY2ExX2dzZWEobXlfdnN0KQpgYGAKCiMjIyBHZW5lcyBEcml2aW5nIFBDMQoKYGBge3J9CgpkYXRhdGFibGUocGMxX3JlcyxvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnKSkpCgpgYGAKCgojIyMgUEMyIEdTRUEKCmBgYHtyfQoKcGNhMl9nc2VhID0gZnVuY3Rpb24ob2JqZWN0KSB7CiAgIyBjYWxjdWxhdGUgdGhlIHZhcmlhbmNlIGZvciBlYWNoIGdlbmUKICBydiA8LSByb3dWYXJzKGFzc2F5KG9iamVjdCkpCgogICMgc2VsZWN0IHRoZSAxMDAwIHRvcCBnZW5lcyBieSB2YXJpYW5jZQogIHNlbGVjdCA8LSBvcmRlcihydiwgZGVjcmVhc2luZz1UUlVFKVtzZXFfbGVuKG1pbigyMDAwLCBsZW5ndGgocnYpKSldCgogICMgcGVyZm9ybSBhIFBDQSBvbiB0aGUgZGF0YSBpbiBhc3NheSh4KSBmb3IgdGhlIHNlbGVjdGVkIGdlbmVzCiAgcGNhIDwtIHByY29tcCh0KGFzc2F5KG9iamVjdClbc2VsZWN0LF0pKQoKICBQQ0ExX2NvbnRyaWIgPC0gc29ydChwY2Ekcm90YXRpb25bLDJdLCBkZWNyZWFzaW5nID0gVFJVRSApCiAgUENBMV9jb250cmliIDwtIGRhdGEuZnJhbWUoImVuc2dlbmUiID0gbmFtZXMoUENBMV9jb250cmliKSwgUENBMV9jb250cmliKQogIFBDQTFfY29udHJpYiA8LSBsZWZ0X2pvaW4oUENBMV9jb250cmliLGdyY2gzOCxieT0iZW5zZ2VuZSIpCiAgUENBMV9jb250cmliIDwtIFBDQTFfY29udHJpYltjb21wbGV0ZS5jYXNlcyhQQ0ExX2NvbnRyaWIkc3ltYm9sKSxdCiAgZm9yX2dzZWEgPC0gUENBMV9jb250cmliJFBDQTFfY29udHJpYiAgCiAgbmFtZXMoZm9yX2dzZWEpIDwtIFBDQTFfY29udHJpYiRzeW1ib2wKICBmZ3NlYVJlcyA8LSBmZ3NlYShoYWxsbWFyaywgZm9yX2dzZWEsIG5wZXJtPTEwMDAwLCBtYXhTaXplID0gNTAwKQogIGZnc2VhUmVzIDwtIGZnc2VhUmVzW2Znc2VhUmVzJHBhZGogPCAwLjA1LF0KICBwbG90R3NlYVRhYmxlKGhhbGxtYXJrW2Znc2VhUmVzJHBhdGh3YXldLCBmb3JfZ3NlYSwgZmdzZWFSZXMsIGdzZWFQYXJhbSA9IDAuNCkKICByZXR1cm4oUENBMV9jb250cmliKQp9CnBjYTJfcmVzIDwtIHBjYTJfZ3NlYShteV92c3QpCmBgYAoKCiMjIyBHZW5lcyBEcml2aW5nIFBDMgoKYGBge3J9CmRhdGF0YWJsZShwY2EyX3JlcyxvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnKSkpCmBgYAoKCiMjIGNvbWJpbmF0aW9ucwoKYGBge3J9CmxpYnJhcnkoZ3Rvb2xzKQpteV9jb21iaW5hdGlvbnMgPC0gZGF0YS5mcmFtZShjb21iaW5hdGlvbnMobj02LHI9Mix2PXVuaXF1ZShzYW1wbGVfdGFibGUkR1JPVVAuTkFNRSkpLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKY29sbmFtZXMobXlfY29tYmluYXRpb25zKSA8LSBjKCJmaXJzdCIsInNlY29uZCIpCgpkYXRhdGFibGUobXlfY29tYmluYXRpb25zKQpgYGAKCiMjIERpZmZlcmVudGlhbCBHZW5lIEV4cHJlc3Npb24KIyMjIDEuCgpgYGB7cn0KIyBkZXNlcSA8LSBERVNlcShkZHMpCiMgc2F2ZVJEUyhkZXNlcSwidnQ0MF9kZXNlcS5yZHMiKQpkZXNlcSA8LSByZWFkUkRTKCJ2dDQwX2Rlc2VxLnJkcyIpCgpER0UgPC0gZnVuY3Rpb24oYzEsYzIpeyAKcmVzIDwtIHJlc3VsdHMoZGVzZXEsY29udHJhc3QgPSBjKCJHUk9VUC5OQU1FIiwgYzEsIGMyKSkKcmVzT3JkZXJlZCA8LSByZXNbb3JkZXIocmVzJHBhZGopLF0KcmVzT3JkZXJlZCA8LSByZXNPcmRlcmVkW2NvbXBsZXRlLmNhc2VzKHJlc09yZGVyZWQpLF0KcmVzT3JkZXJlZCA8LSBkYXRhLmZyYW1lKHJlc09yZGVyZWQpCnJlc09yZGVyZWQgPC0gbGVmdF9qb2luKHJvd25hbWVzX3RvX2NvbHVtbihyZXNPcmRlcmVkLHZhciA9ICJlbnNnZW5lIiksIGdyY2gzOCwgYnkgPSAiZW5zZ2VuZSIpICU+JSBzZWxlY3Qoc3ltYm9sLGxvZzJGb2xkQ2hhbmdlLHBhZGopCgpyZXNfZm9yX3RhYmxlIDwtIHJlc09yZGVyZWQgICU+JSBkcGx5cjo6ZmlsdGVyKHBhZGogPCAwLjEpCgpyZXR1cm4obGlzdChyZXNPcmRlcmVkPXJlc09yZGVyZWQscmVzX2Zvcl90YWJsZT1yZXNfZm9yX3RhYmxlKSkKfQoKbXlfcmVzPC1ER0UobXlfY29tYmluYXRpb25zJGZpcnN0WzFdLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbMV0pCmRhdGF0YWJsZShteV9yZXMkcmVzX2Zvcl90YWJsZSwgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywKICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZShteV9jb21iaW5hdGlvbnMkZmlyc3RbMV0sIiB2cyAiLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbMV0pLAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ0JmcnRpcCcsCiAgICAgICAgICAgICAgICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnKSkpCmBgYAoKCmBgYHtyfQpwbG90X2dzZWEgPC0gZnVuY3Rpb24ocmVzKXsKcmVzIDwtIHJlc1tjb21wbGV0ZS5jYXNlcyhyZXMpLF0KcmVzJGZjU2lnbiA8LSBzaWduKHJlcyRsb2cyRm9sZENoYW5nZSkKcmVzJGxvZ1AgPC0gLWxvZzEwKHJlcyRwYWRqKQpyZXMkbWV0cmljIDwtIHJlcyRsb2dQL3JlcyRmY1NpZ24KeTwtcmVzWyxjKCJzeW1ib2wiLCAibWV0cmljIildCmdlbmVMaXN0IDwtIHkkbWV0cmljCm5hbWVzKGdlbmVMaXN0KSA8LSB5JHN5bWJvbApnZW5lTGlzdCA8LSBnZW5lTGlzdFtvcmRlcihnZW5lTGlzdCwgZGVjcmVhc2luZyA9IFQpXQpmZ3NlYVJlcyA8LSBmZ3NlYShoYWxsbWFyaywgZ2VuZUxpc3QsIG5wZXJtPTEwMDAwLCBtYXhTaXplID0gNTAwKQpmZ3NlYVJlcyA8LSBmZ3NlYVJlc1tmZ3NlYVJlcyRwYWRqIDwgMC4wNSxdCnBsb3RHc2VhVGFibGUoaGFsbG1hcmtbZmdzZWFSZXMkcGF0aHdheV0sIGdlbmVMaXN0LCBmZ3NlYVJlcywgZ3NlYVBhcmFtID0gMC40KQp9CgpwbG90X2dzZWEobXlfcmVzJHJlc09yZGVyZWQpCmBgYAoKIyMjIDIuCgpgYGB7cn0KbXlfcmVzPC1ER0UobXlfY29tYmluYXRpb25zJGZpcnN0WzJdLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbMl0pCmRhdGF0YWJsZShteV9yZXMkcmVzX2Zvcl90YWJsZSwgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywKICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZShteV9jb21iaW5hdGlvbnMkZmlyc3RbMl0sIiB2cyAiLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbMl0pLAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ0JmcnRpcCcsYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJykpKQpgYGAKCgpgYGB7cn0KcGxvdF9nc2VhKG15X3JlcyRyZXNPcmRlcmVkKQpgYGAKCiMjIyAzLgoKYGBge3J9Cm15X3JlczwtREdFKG15X2NvbWJpbmF0aW9ucyRmaXJzdFszXSxteV9jb21iaW5hdGlvbnMkc2Vjb25kWzNdKQpkYXRhdGFibGUobXlfcmVzJHJlc19mb3JfdGFibGUsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsCiAgICAgICAgICBjYXB0aW9uID0gcGFzdGUobXlfY29tYmluYXRpb25zJGZpcnN0WzNdLCIgdnMgIixteV9jb21iaW5hdGlvbnMkc2Vjb25kWzNdKSwKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcpKSkKYGBgCgoKYGBge3J9CnBsb3RfZ3NlYShteV9yZXMkcmVzT3JkZXJlZCkKYGBgCgojIyMgNC4KCmBgYHtyfQpteV9yZXM8LURHRShteV9jb21iaW5hdGlvbnMkZmlyc3RbNF0sbXlfY29tYmluYXRpb25zJHNlY29uZFs0XSkKZGF0YXRhYmxlKG15X3JlcyRyZXNfZm9yX3RhYmxlLCBleHRlbnNpb25zID0gJ0J1dHRvbnMnLAogICAgICAgICAgY2FwdGlvbiA9IHBhc3RlKG15X2NvbWJpbmF0aW9ucyRmaXJzdFs0XSwiIHZzICIsbXlfY29tYmluYXRpb25zJHNlY29uZFs0XSksCiAgICAgICAgICBvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnKSkpCmBgYAoKCmBgYHtyfQpwbG90X2dzZWEobXlfcmVzJHJlc09yZGVyZWQpCmBgYAoKIyMjIDUuCgpgYGB7cn0KbXlfcmVzPC1ER0UobXlfY29tYmluYXRpb25zJGZpcnN0WzVdLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbNV0pCmRhdGF0YWJsZShteV9yZXMkcmVzX2Zvcl90YWJsZSwgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywKICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZShteV9jb21iaW5hdGlvbnMkZmlyc3RbNV0sIiB2cyAiLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbNV0pLAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ0JmcnRpcCcsYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJykpKQpgYGAKCgpgYGB7cn0KcGxvdF9nc2VhKG15X3JlcyRyZXNPcmRlcmVkKQpgYGAKCiMjIyA2LgoKYGBge3J9Cm15X3JlczwtREdFKG15X2NvbWJpbmF0aW9ucyRmaXJzdFs2XSxteV9jb21iaW5hdGlvbnMkc2Vjb25kWzZdKQpkYXRhdGFibGUobXlfcmVzJHJlc19mb3JfdGFibGUsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsCiAgICAgICAgICBjYXB0aW9uID0gcGFzdGUobXlfY29tYmluYXRpb25zJGZpcnN0WzZdLCIgdnMgIixteV9jb21iaW5hdGlvbnMkc2Vjb25kWzZdKSwKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcpKSkKYGBgCgoKYGBge3J9CnBsb3RfZ3NlYShteV9yZXMkcmVzT3JkZXJlZCkKYGBgCgojIyMgNy4KCmBgYHtyfQpteV9yZXM8LURHRShteV9jb21iaW5hdGlvbnMkZmlyc3RbN10sbXlfY29tYmluYXRpb25zJHNlY29uZFs3XSkKZGF0YXRhYmxlKG15X3JlcyRyZXNfZm9yX3RhYmxlLCBleHRlbnNpb25zID0gJ0J1dHRvbnMnLAogICAgICAgICAgY2FwdGlvbiA9IHBhc3RlKG15X2NvbWJpbmF0aW9ucyRmaXJzdFs3XSwiIHZzICIsbXlfY29tYmluYXRpb25zJHNlY29uZFs3XSksCiAgICAgICAgICBvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnKSkpCmBgYAoKCmBgYHtyfQpwbG90X2dzZWEobXlfcmVzJHJlc09yZGVyZWQpCmBgYAoKIyMjIDguCgpgYGB7cn0KbXlfcmVzPC1ER0UobXlfY29tYmluYXRpb25zJGZpcnN0WzhdLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbOF0pCmRhdGF0YWJsZShteV9yZXMkcmVzX2Zvcl90YWJsZSwgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywKICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZShteV9jb21iaW5hdGlvbnMkZmlyc3RbOF0sIiB2cyAiLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbOF0pLAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ0JmcnRpcCcsYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJykpKQpgYGAKCgpgYGB7cn0KcGxvdF9nc2VhKG15X3JlcyRyZXNPcmRlcmVkKQpgYGAKCiMjIyA5LgoKYGBge3J9Cm15X3JlczwtREdFKG15X2NvbWJpbmF0aW9ucyRmaXJzdFs5XSxteV9jb21iaW5hdGlvbnMkc2Vjb25kWzldKQpkYXRhdGFibGUobXlfcmVzJHJlc19mb3JfdGFibGUsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsCiAgICAgICAgICBjYXB0aW9uID0gcGFzdGUobXlfY29tYmluYXRpb25zJGZpcnN0WzldLCIgdnMgIixteV9jb21iaW5hdGlvbnMkc2Vjb25kWzldKSwKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcpKSkKYGBgCgoKYGBge3J9CnBsb3RfZ3NlYShteV9yZXMkcmVzT3JkZXJlZCkKYGBgCgoKIyMjIDEwLgoKYGBge3J9Cm15X3JlczwtREdFKG15X2NvbWJpbmF0aW9ucyRmaXJzdFsxMF0sbXlfY29tYmluYXRpb25zJHNlY29uZFsxMF0pCmRhdGF0YWJsZShteV9yZXMkcmVzX2Zvcl90YWJsZSwgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywKICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZShteV9jb21iaW5hdGlvbnMkZmlyc3RbMTBdLCIgdnMgIixteV9jb21iaW5hdGlvbnMkc2Vjb25kWzEwXSksCiAgICAgICAgICBvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnKSkpCmBgYAoKCmBgYHtyfQpwbG90X2dzZWEobXlfcmVzJHJlc09yZGVyZWQpCmBgYAoKCiMjIyAxMS4KCmBgYHtyfQpteV9yZXM8LURHRShteV9jb21iaW5hdGlvbnMkZmlyc3RbMTFdLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbMTFdKQpkYXRhdGFibGUobXlfcmVzJHJlc19mb3JfdGFibGUsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsCiAgICAgICAgICBjYXB0aW9uID0gcGFzdGUobXlfY29tYmluYXRpb25zJGZpcnN0WzExXSwiIHZzICIsbXlfY29tYmluYXRpb25zJHNlY29uZFsxMV0pLAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ0JmcnRpcCcsYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJykpKQpgYGAKCgpgYGB7cn0KcGxvdF9nc2VhKG15X3JlcyRyZXNPcmRlcmVkKQpgYGAKCgoKIyMjIDEyLgoKYGBge3J9Cm15X3JlczwtREdFKG15X2NvbWJpbmF0aW9ucyRmaXJzdFsxMl0sbXlfY29tYmluYXRpb25zJHNlY29uZFsxMl0pCmRhdGF0YWJsZShteV9yZXMkcmVzX2Zvcl90YWJsZSwgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywKICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZShteV9jb21iaW5hdGlvbnMkZmlyc3RbMTJdLCIgdnMgIixteV9jb21iaW5hdGlvbnMkc2Vjb25kWzEyXSksCiAgICAgICAgICBvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnKSkpCmBgYAoKCmBgYHtyfQpwbG90X2dzZWEobXlfcmVzJHJlc09yZGVyZWQpCmBgYAoKCgojIyMgMTMuCgpgYGB7cn0KbXlfcmVzPC1ER0UobXlfY29tYmluYXRpb25zJGZpcnN0WzEzXSxteV9jb21iaW5hdGlvbnMkc2Vjb25kWzEzXSkKZGF0YXRhYmxlKG15X3JlcyRyZXNfZm9yX3RhYmxlLCBleHRlbnNpb25zID0gJ0J1dHRvbnMnLAogICAgICAgICAgY2FwdGlvbiA9IHBhc3RlKG15X2NvbWJpbmF0aW9ucyRmaXJzdFsxM10sIiB2cyAiLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbMTNdKSwKICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcpKSkKYGBgCgoKYGBge3J9CnBsb3RfZ3NlYShteV9yZXMkcmVzT3JkZXJlZCkKYGBgCgoKIyMjIDE0LgoKCmBgYHtyfQpteV9yZXM8LURHRShteV9jb21iaW5hdGlvbnMkZmlyc3RbMTRdLG15X2NvbWJpbmF0aW9ucyRzZWNvbmRbMTRdKQpkYXRhdGFibGUobXlfcmVzJHJlc19mb3JfdGFibGUsIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsCiAgICAgICAgICBjYXB0aW9uID0gcGFzdGUobXlfY29tYmluYXRpb25zJGZpcnN0WzE0XSwiIHZzICIsbXlfY29tYmluYXRpb25zJHNlY29uZFsxNF0pLAogICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ0JmcnRpcCcsYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJykpKQpgYGAKCgpgYGB7cn0KcGxvdF9nc2VhKG15X3JlcyRyZXNPcmRlcmVkKQpgYGAKCgojIyMgMTUuCgoKYGBge3J9Cm15X3JlczwtREdFKG15X2NvbWJpbmF0aW9ucyRmaXJzdFsxNV0sbXlfY29tYmluYXRpb25zJHNlY29uZFsxNV0pCmRhdGF0YWJsZShteV9yZXMkcmVzX2Zvcl90YWJsZSwgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywKICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZShteV9jb21iaW5hdGlvbnMkZmlyc3RbMTVdLCIgdnMgIixteV9jb21iaW5hdGlvbnMkc2Vjb25kWzE1XSksCiAgICAgICAgICBvcHRpb25zID0gbGlzdChkb20gPSAnQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnKSkpCmBgYAoKCmBgYHtyfQpwbG90X2dzZWEobXlfcmVzJHJlc09yZGVyZWQpCmBgYA==